require 'digest/sha1'
class User < ActiveRecord::Base
  # quando não existir usuário logado, o usuário com este id será carregado
  ID_VISITANTE=2
  # O USUÁRIO COM ESTE ID TERÁ PERMISSÃO PARA FAZER TUDO NO SISTEMA
  ID_SUPER_USUARIO=3
  # Virtual attribute for the unencrypted password
  attr_accessor :password
  
  belongs_to :grupo
  
  validates_presence_of     :login, :email
  validates_presence_of     :password,                   :if => :password_required?
  validates_presence_of     :password_confirmation,      :if => :password_required?
  validates_length_of       :password, :within => 4..40, :if => :password_required?
  validates_confirmation_of :password,                   :if => :password_required?
  validates_length_of       :login,    :within => 3..40
  validates_length_of       :email,    :within => 3..100
  validates_uniqueness_of   :login, :email, :case_sensitive => false
  before_save :encrypt_password
  
  # prevents a user from submitting a crafted form that bypasses activation
  # anything else you want your user to change should be added here.
  attr_accessible :login, :email, :password, :password_confirmation

  acts_as_state_machine :initial => :pending
  state :passive
  state :pending, :enter => :make_activation_code
  state :active,  :enter => :do_activate
  state :suspended
  state :deleted, :enter => :do_delete

  event :register do
    transitions :from => :passive, :to => :pending, :guard => Proc.new {|u| !(u.crypted_password.blank? && u.password.blank?) }
  end
  
  event :activate do
    transitions :from => :pending, :to => :active 
  end
  
  event :suspend do
    transitions :from => [:passive, :pending, :active], :to => :suspended
  end
  
  event :delete do
    transitions :from => [:passive, :pending, :active, :suspended], :to => :deleted
  end

  event :unsuspend do
    transitions :from => :suspended, :to => :active,  :guard => Proc.new {|u| !u.activated_at.blank? }
    transitions :from => :suspended, :to => :pending, :guard => Proc.new {|u| !u.activation_code.blank? }
    transitions :from => :suspended, :to => :passive
  end

  # Authenticates a user by their login name and unencrypted password.  Returns the user or nil.
  def self.authenticate(login, password)
    u = find_in_state :first, :active, :conditions => {:login => login} # need to get the salt
    u && u.authenticated?(password) ? u : nil
  end

  # Encrypts some data with the salt.
  def self.encrypt(password, salt)
    Digest::SHA1.hexdigest("--#{salt}--#{password}--")
  end

  # Encrypts the password with the user salt
  def encrypt(password)
    self.class.encrypt(password, salt)
  end

  def authenticated?(password)
    crypted_password == encrypt(password)
  end

  def remember_token?
    remember_token_expires_at && Time.now.utc < remember_token_expires_at 
  end

  # These create and unset the fields required for remembering users between browser closes
  def remember_me
    remember_me_for 2.weeks
  end

  def remember_me_for(time)
    remember_me_until time.from_now.utc
  end

  def remember_me_until(time)
    self.remember_token_expires_at = time
    self.remember_token            = encrypt("#{email}--#{remember_token_expires_at}")
    save(false)
  end

  def forget_me
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end
###########################################
###########################################
###########################################

  def self.id_visitante
    User::ID_VISITANTE
  end

#  def self.criar_codigo_nova_senha
#    Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
#  end
#
#  def self.criar_nova_senha
#    "oriente"
#  end

  def self.visitante
    User.find(User::ID_VISITANTE)
  end

  def visitante?
    self.id == User::ID_VISITANTE
  end

  def super_usuario?
    self.id == User::ID_SUPER_USUARIO
  end
  
  def tem_permissao?(controller,action,id)
    #super usuario tem permissão pra tudo.
    return true if self.super_usuario?

    nome_controller = controller + '_controller'
    papel = Papel.find(:first,:conditions=>"direito.controller='#{nome_controller}' AND direito.action='#{action}'",:include=>'direito')#:joins=>'p inner join direito d on d.papel_id = p.id'
    if(!papel)
      raise "Esta acao -#{nome_controller}-#{action}- nao pertence a nenhum papel"
    end
    if(papel.especifico_conteudo)
      if(id.nil?) 
        msg = "Esta acao -#{nome_controller}-#{action}- pertence a um papel com conteudo especifico #{papel.nome} e o id conteudo nao foi especificado. Nao ha como verificar a permissao para este grupo : #{self.grupo.nome}"
        raise msg
      end
      # este comentário se removido gera um bug.
    end
    result = self.connection.select_one(sql_permissao(nome_controller,action,id))
    (result['tem_permissao'].to_i > 0 )
  end

  def sql_permissao(controller,action,id)
    sql = "SELECT count(*) as tem_permissao"<<
    " FROM direito d"<<
    " INNER JOIN papel p ON p.id = d.papel_id"<<
    " AND d.controller = '#{controller}'"<<
    " AND d.action = '#{action}'"<<
    " INNER JOIN permissao perm ON perm.papel_id = p.id AND perm.tem_acesso = 1"
    sql += ( (!id.nil?) ? " AND perm.conteudo_id = #{id.to_s}" : '')
    sql << " INNER JOIN grupo g ON g.id = perm.grupo_id"<<
    " INNER JOIN user u ON u.grupo_id = g.id"<<
    " AND u.id =#{self.id}"
    sql 
  end


  #forcar_todos => se true, inclui todos os itens no menu
  def filtrar_itens_menu(ca_menu)
    puts('-----------------------------FILTRAR_ITENS_MENU-USER...')
    itens = ca_menu.itens_raiz
    self.filtrar_itens_recursivo(itens,self.super_usuario?)
  end

  def filtrar_itens_recursivo(itens,forcar_todos)
    itens_com_acesso = []
    itens.each do |ca_menu_item|
      if( forcar_todos || self.tem_acesso_menu_item(ca_menu_item) )
        itens_com_acesso << ca_menu_item
        itens_com_acesso.concat(filtrar_itens_recursivo(ca_menu_item.children,forcar_todos))
      end
    end
    itens_com_acesso
  end

  def tem_acesso_menu_item(ca_menu_item)
    logger.warn('===============================================')
    result = self.connection.select_one(sql_acesso_menu(ca_menu_item.id))
    (result['tem_acesso'].to_i > 0 )
  end

  def sql_acesso_menu(menu_item_id)
    "SELECT count(*) as tem_acesso" <<
    " FROM user u" <<
    " INNER JOIN grupo g ON u.grupo_id = g.id AND u.id = #{self.id}" <<
    " INNER JOIN permissao perm ON perm.grupo_id = g.id" << #como não especifica conteudo_id, pode ser qualquer conteudo
    " INNER JOIN papel p ON perm.papel_id = p.id" << 
    " INNER JOIN ca_menu_item_papel mp ON mp.papel_id = p.id" <<
    " AND mp.ca_menu_item_id = '#{menu_item_id}'"
  end
  ####################################
  ###########################################
  ###########################################
  protected
    # before filter 
    def encrypt_password
      return if password.blank?
      self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if new_record?
      self.crypted_password = encrypt(password)
    end
      
    def password_required?
      crypted_password.blank? || !password.blank?
    end
    
    def make_activation_code
      self.deleted_at = nil
      self.activation_code = Digest::SHA1.hexdigest( Time.now.to_s.split(//).sort_by {rand}.join )
    end
    
    def do_delete
      raise "Remover o super usuário pode causar sérios problemas." if self.id == User::ID_SUPER_USUARIO
      raise "Remover o usuário visitante pode causar sérios problemas." if self.id == User::ID_VISITANTE      
      self.deleted_at = Time.now.utc
    end

    def do_activate
      self.activated_at = Time.now.utc
      self.deleted_at = self.activation_code = nil
    end
    
  #########################################################
  
  
    
end
